package com.wasu.realm;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import com.wasu.service.OrderJedis;

/**
 * 构造并返回 AuthenticationInfo对象,通过 HashedCredentialsMatcher的doCredentialsMatch()方法自动完成认证工作
 * 最终比较的是：数据库查询的密码credentials 和 用户输入的password 是否一致， password会用applicationContext.xml 的credentialsMatcher 完成对password的加密
 * 加密方式MD5（xml配置），加密次数（xml配置）， 盐值 (AuthenticationInfo传入)
 * 
 * 多realm认证策略： 根据 applicationContext.xml   ModularRealmAuthenticator配置的顺序进行验证，有一个成功就可以了
 * 
 * Shiro的盐值加密 md5($salt.$pass)	 和阿里巴巴的MD5 md5($pass.$salt)
 * 
 * @author zhouyang
 *
 */
public class FirstRealm extends AuthorizingRealm {
	private static Logger log = Logger.getLogger(FirstRealm.class);
	/**
	 * 认证
	 */
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		//1、把AuthenticationToken 还原为 UsernamePasswordToken 对象
		UsernamePasswordToken upt = (UsernamePasswordToken) token;
		//2、从 UsernamePasswordToken 获取username 
		String username= upt.getUsername();
		
		//3、从数据库卡查询username对应的password
		Object credentials =null;
		if(OrderJedis.getUsers("users").contains(username)){
			credentials = OrderJedis.getPassword("users", username);
		}else{
			//4、如果用户不存在，则抛出UnknownAccountException
			throw new UnknownAccountException("未知用户");
		}
		
		//5、根据用户的信息，决定是否抛出其他异常
		//6、根据用户的信息，来构建AuthenticationInfo
		//principals:认证的实体信息，可以是username，也可以是数据表对应的用户实体对象
		//hashedCredentials:密码
		//realmName:当前realm对象的name，调用父类的getName()
		Object principal =username;
		
//		SecureRandomNumberGenerator secureRandom = new SecureRandomNumberGenerator();
//		String salt = secureRandom.nextBytes(3).toHex();
		ByteSource credentialsSalt =ByteSource.Util.bytes(username); //盐值：以用户名作为盐值对密码进行MD5加密， 如果不使用盐值，那么存贮在数据库的密码的MD5后的字符串一样，用户存储用户密码时保证每个用户的用户名、密码都唯一，更安全
		
//		AuthenticationInfo info = new SimpleAccount(principal, credentials, getName());//简单MD5校验
		
		
		AuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, getName()); //带盐值MD5校验
		
		return info;
	}

	/**
	 * 授权
	 */
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		//1、从PrincipalCollection中获取登录用户的信息
		Object principal = principals.getPrimaryPrincipal(); //用户名
		//2、利用登录的用户的信息获取当前用户的角色or权限（可能需要查询数据库）
		Set<String> roles = new HashSet<String>();
		//
		List<String> values =OrderJedis.getValues(principal.toString());
		for(String value:values){
			roles.add(value);
		}
		//3、创建SimpleAuthorizationInfo对象，并设置其roles属性
		SimpleAuthorizationInfo info  = new SimpleAuthorizationInfo();
		info.setRoles(roles);
		//4、返回SimpleAuthorizationInfo对象
		return info;
	}
	
	//清除缓存
    public void clearCached() {
        PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
        super.clearCache(principals);
    }
    
}
